<?php

namespace DreamCat\PropertyAnalysis\Test;

use DreamCat\PropertyAnalysis\DemoClass\DemoInterface;
use DreamCat\PropertyAnalysis\DemoClass\Inner\Demo;
use DreamCat\PropertyAnalysis\DemoClass\Pojo1;
use DreamCat\PropertyAnalysis\DemoClass\WithMapField;
use Dreamcat\PropertyAnalysis\Pojo\AnalysisResult;
use Dreamcat\PropertyAnalysis\Pojo\BuildinType;
use Dreamcat\PropertyAnalysis\Pojo\OneMayTypeDesc;
use Dreamcat\PropertyAnalysis\Pojo\PropertyResult;
use Dreamcat\PropertyAnalysis\Pojo\TypeDescription;
use Dreamcat\PropertyAnalysis\PropertyAnalysis;
use PHPUnit\Framework\TestCase;
use ReflectionClass;
use ReflectionException;
use ReflectionMethod;
use ReflectionProperty;

/**
 * 测试代码
 * @author vijay
 */
class AnalysisTest extends TestCase
{
    /**
     * -
     * @return void
     * @throws ReflectionException
     */
    public function testNormal(): void
    {
        $class = Pojo1::class;
        $expect = $this->buildPojo1Check();
        $analysis = new PropertyAnalysis();
        $result = $analysis->analysis(new ReflectionClass($class));
        self::assertTrue($analysis->isAnalysisSetter());
        self::assertEquals($expect, $result, "第1条测试用例 -1 未通过");
        self::assertEquals($class, $result->getClassName());
        $second = $analysis->analysis(new ReflectionClass($class));
        self::assertEquals(
            spl_object_id($result),
            spl_object_id($second),
            "第1条测试用例未使用缓存"
        );

        $list = $expect->getPropertyList();
        foreach ($list as $key => $val) {
            $new = new PropertyResult($val->getProperty());
            if ($val->getTypeDescription()) {
                $new->setTypeDescription($val->getTypeDescription());
            }
            $list[$key] = $new;
        }
        $expect->setPropertyList($list);
        $analysis->setAnalysisSetter(false);
        self::assertFalse($analysis->isAnalysisSetter());
        $result = $analysis->analysis(new ReflectionClass($class));
        self::assertEquals($expect, $result, "第1条测试用例 -2 未通过");
    }

    private function buildPojo1Check(): AnalysisResult
    {
        $result = new AnalysisResult(Pojo1::class);
        $list = [];

        $proResult = new PropertyResult(new ReflectionProperty(Pojo1::class, "field1"));
        $proResult->setTypeDescription(new TypeDescription($proResult->getProperty()->getType(), null, false));
        $proResult->getTypeDescription()->addTypeDescription(new OneMayTypeDesc(BuildinType::INT));
        $setRef = new ReflectionMethod(Pojo1::class, "setField1");
        $proResult->setSetter($setRef);
        $proResult->setSetterSameClass(true);
        $proResult->setSetterTypeDescription(new TypeDescription($setRef->getParameters()[0]->getType()));
        $proResult->getSetterTypeDescription()
            ->addTypeDescription(new OneMayTypeDesc(BuildinType::INT));
        $list["field1"] = $proResult;

        $proResult = new PropertyResult(new ReflectionProperty(Pojo1::class, "stringArray"));
        $proResult->setTypeDescription(new TypeDescription($proResult->getProperty()->getType(), null, false));
        $one = new OneMayTypeDesc(BuildinType::ARRAY);
        $one->setArrayElement(new TypeDescription());
        $one->getArrayElement()->addTypeDescription(new OneMayTypeDesc(BuildinType::STRING));
        $proResult->getTypeDescription()->addTypeDescription($one);
        $list["stringArray"] = $proResult;

        $proResult = new PropertyResult(new ReflectionProperty(Pojo1::class, "mixedArray"));
        $proResult->setTypeDescription(new TypeDescription($proResult->getProperty()->getType(), null, false));
        $proResult->getTypeDescription()->addTypeDescription(new OneMayTypeDesc(BuildinType::ARRAY));
        $list["mixedArray"] = $proResult;

        $proResult = new PropertyResult(new ReflectionProperty(Pojo1::class, "demo"));
        $proResult->setTypeDescription(new TypeDescription($proResult->getProperty()->getType(), null, false));
        $proResult->getTypeDescription()->addTypeDescription(new OneMayTypeDesc(new ReflectionClass(Demo::class)));
        $list["demo"] = $proResult;

        $proResult = new PropertyResult(new ReflectionProperty(Pojo1::class, "demos"));
        $proResult->setTypeDescription(new TypeDescription($proResult->getProperty()->getType(), null, false));
        $one = new OneMayTypeDesc(BuildinType::ARRAY);
        $one->setArrayElement(new TypeDescription());
        $one->getArrayElement()->addTypeDescription(new OneMayTypeDesc(new ReflectionClass(Demo::class)));
        $proResult->getTypeDescription()->addTypeDescription($one);
        $list["demos"] = $proResult;

        $proResult = new PropertyResult(new ReflectionProperty(Pojo1::class, "demoInterface"));
        $proResult->setTypeDescription(new TypeDescription($proResult->getProperty()->getType(), null, false));
        $proResult->getTypeDescription()->addTypeDescription(
            new OneMayTypeDesc(new ReflectionClass(DemoInterface::class))
        );
        $list["demoInterface"] = $proResult;

        $proResult = new PropertyResult(new ReflectionProperty(Pojo1::class, "union"));
        $proResult->setTypeDescription(new TypeDescription($proResult->getProperty()->getType(), null, false));
        $proResult->getTypeDescription()->addTypeDescription(new OneMayTypeDesc(BuildinType::STRING));
        $proResult->getTypeDescription()->addTypeDescription(new OneMayTypeDesc(BuildinType::INT));
        $list["union"] = $proResult;

        $proResult = new PropertyResult(new ReflectionProperty(Pojo1::class, "hasDefault"));
        $proResult->setTypeDescription(
            new TypeDescription(
                $proResult->getProperty()->getType(),
                $proResult->getProperty()->getDefaultValue()
            )
        );
        $proResult->getTypeDescription()->addTypeDescription(new OneMayTypeDesc(BuildinType::FLOAT));
        $list["hasDefault"] = $proResult;

        $proResult = new PropertyResult(new ReflectionProperty(Pojo1::class, "mixed"));
        $list["mixed"] = $proResult;

        $proResult = new PropertyResult(new ReflectionProperty(Pojo1::class, "unkownArray"));
        $proResult->setTypeDescription(new TypeDescription($proResult->getProperty()->getType(), null, false));
        $proResult->getTypeDescription()->addTypeDescription(new OneMayTypeDesc(BuildinType::ARRAY));
        $list["unkownArray"] = $proResult;

        $proResult = new PropertyResult(new ReflectionProperty(Pojo1::class, "double"));
        $proResult->setTypeDescription(
            new TypeDescription(
                $proResult->getProperty()->getType(),
                $proResult->getProperty()->getDefaultValue(),
                false
            )
        );
        $proResult->getTypeDescription()->addTypeDescription(new OneMayTypeDesc(BuildinType::INT));
        $list["double"] = $proResult;
        $result->setPropertyList($list);
        return $result;
    }

    private function buildWithmapCheck() :AnalysisResult{
        $result = new AnalysisResult(WithMapField::class);
        $list = [];
        $map = new PropertyResult(new ReflectionProperty(WithMapField::class, "userMap"));
        $map->setTypeDescription(new TypeDescription($map->getProperty()->getType(), $map->getProperty()->getDefaultValue(), false));
        $map->getTypeDescription()->addTypeDescription(new OneMayTypeDesc(BuildinType::ARRAY));
        $arrayType = new TypeDescription();
        $arrayType->addTypeDescription(new OneMayTypeDesc(BuildinType::INT));
        $map->getTypeDescription()
            ->getTypeDescription(BuildinType::ARRAY, false)
            ->setIsMap(true)
            ->setArrayElement($arrayType);
        $list["userMap"] = $map;
        $result->setPropertyList($list);
        return $result;
    }

    /**
     * @throws ReflectionException
     */
    public function testWithMap()
    {
        $class = WithMapField::class;
        $analysis = new PropertyAnalysis();
        $result = $analysis->analysis(new ReflectionClass($class));
        $except = $this->buildWithmapCheck();
        self::assertEquals($except, $result);
    }
}

# end of file
